package cn.springcloud.fix.oauth2.autoconfigure.resource;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.util.StringUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;

@Getter
@Setter
@ConfigurationProperties(prefix = "security.oauth2.resource")
@Deprecated
public class ResourceServerProperties implements BeanFactoryAware, InitializingBean {

    @JsonIgnore
    private final String clientId;

    @JsonIgnore
    private final String clientSecret;

    @JsonIgnore
    private ListableBeanFactory beanFactory;

    private String serviceId = "resource";

    /**
     * Identifier of the resource.
     */
    private String id;

    /**
     * URI of the user endpoint.
     */
    private String userInfoUri;

    /**
     * token-info-uri: http://127.0.0.1:8080/oauth/check_token
     */
    private String tokenInfoUri;

    /**
     * Use the token info, can be set to false to use the user info.
     */
    private boolean preferTokenInfo = true;

    /**
     * The token type to send when using the userInfoUri.
     */
    private String tokenType = "Bearer";

    private Jwt jwt = new Jwt();

    private Jwk jwk = new Jwk();

    public ResourceServerProperties(String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    public void afterPropertiesSet() {
        validate();
    }

    private void validate() {
        if (countBeans(AuthorizationServerEndpointsConfiguration.class) > 0) {
            // If we are an authorization server we don't need remote resource token services
            return;
        }
        //如果加了 @EnableOAuth2Sso 注解会 Import ResourceServerTokenServicesConfiguration
        if (countBeans(ResourceServerTokenServicesConfiguration.class) == 0) {
            // If we are not a resource server or an SSO client we don't need remote resource token services
            return;
        }
        if (!StringUtils.hasText(this.clientId)) {
            return;
        }
        try {
            doValidate();
        } catch (BindException ex) {
            throw new IllegalStateException(ex);
        }
    }

    private int countBeans(Class<?> type) {
        return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, type, true, false).length;
    }

    private void doValidate() throws BindException {
        BindingResult errors = new BeanPropertyBindingResult(this, "resourceServerProperties");
        boolean jwtConfigPresent = StringUtils.hasText(this.jwt.getKeyUri()) || StringUtils.hasText(this.jwt.getKeyValue());
        boolean jwkConfigPresent = StringUtils.hasText(this.jwk.getKeySetUri());

        if (jwtConfigPresent && jwkConfigPresent) {
            errors.reject("ambiguous.keyUri", "Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should" + " be configured.");
        }

        if (!jwtConfigPresent && !jwkConfigPresent) {
            if (!StringUtils.hasText(this.userInfoUri) && !StringUtils.hasText(this.tokenInfoUri)) {
                errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri", "Missing tokenInfoUri and userInfoUri and there is no " + "JWT verifier key");
            }
            if (StringUtils.hasText(this.tokenInfoUri) && isPreferTokenInfo()) {
                if (!StringUtils.hasText(this.clientSecret)) {
                    errors.rejectValue("clientSecret", "missing.clientSecret", "Missing client secret");
                }
            }
        }

        if (errors.hasErrors()) {
            throw new BindException(errors);
        }
    }

    @Getter
    @Setter
    public class Jwt {

        /**
         * The verification key of the JWT token. Can either be a symmetric secret or
         * PEM-encoded RSA public key. If the value is not available, you can set the URI
         * instead.
         */
        private String keyValue;

        /**
         * The URI of the JWT token. Can be set if the value is not available and the key
         * is public.
         */
        private String keyUri;

        /**
         * The location of the key store.
         */
        private String keyStore;

        /**
         * The key store's password
         */
        private String keyStorePassword;

        /**
         * The alias of the key from the key store
         */
        private String keyAlias;

        /**
         * The password of the key from the key store
         */
        private String keyPassword;
    }

    @Getter
    @Setter
    public class Jwk {

        /**
         * The URI to get verification keys to verify the JWT token. This can be set when
         * the authorization server returns a set of verification keys.
         */
        private String keySetUri;
    }
}

